SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
9.2 KB · 164 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { notFound } from 'next/navigation';4import { orderedSections, QuietLine, TODAY_FEED_HREF, TODAY_LABELS, TodayItemRow } from '@/components/changes/today-sections';5import { DataStrip, SectionNav } from '@/components/layout/terminal';6import { BreadcrumbLd } from '@/components/meta/breadcrumb-ld';7import { ImportanceMark } from '@/components/ui/badges';8import { EntityLink } from '@/components/ui/entity';9import { Hint } from '@/components/ui/hint';10import { Container, Note, PageHeader, Section } from '@/components/ui/section';11import { EmptyState, Unavailable } from '@/components/ui/unavailable';12import { apiD3, safe } from '@/lib/api';13import { fmtDate, fmtInt, num } from '@/lib/format';14import { routes, SITE_NAME, SITE_URL } from '@/lib/site';1516type Params = { params: Promise<{ date: string }>; searchParams: Promise<{ include_backfill?: string }> };17const VALID = /^\d{4}-\d{2}-\d{2}$/;1819function shift(date: string, days: number): string {20  const d = new Date(`${date}T00:00:00Z`);21  d.setUTCDate(d.getUTCDate() + days);22  return d.toISOString().slice(0, 10);23}2425export async function generateMetadata({ params }: Params): Promise<Metadata> {26  const { date } = await params;27  if (!VALID.test(date)) return { title: 'Not found', robots: { index: false } };28  const title = `Today in AI — ${fmtDate(date)}`;29  const description = `What happened in AI on ${fmtDate(date)}: major releases, price moves, benchmark moves, model changes, research, open-weight releases, deprecations, provider and hardware changes — generated from the ${SITE_NAME} change log.`;30  return { title, description, alternates: { canonical: routes.changesDay(date) }, openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${routes.changesDay(date)}`, type: 'article' } };31}3233export default async function DailyPage({ params, searchParams }: Params) {34  const { date } = await params;35  const sp = await searchParams;36  if (!VALID.test(date) || Number.isNaN(new Date(`${date}T00:00:00Z`).getTime())) notFound();37  const today = new Date().toISOString().slice(0, 10);38  if (date > today) notFound();39  const backfill = sp.include_backfill === '1';40  const d = await safe(apiD3.changesDaily(date, 30, backfill));41  const { present, quiet } = orderedSections(d?.today);42  const total = num(d?.total) ?? (d ? Object.values(d.counts ?? {}).reduce<number>((n, v) => n + (num(v) ?? 0), 0) : 0);43  const excluded = num(d?.backfill_excluded) ?? 0;44  const prev = d?.previous_day ?? shift(date, -1);45  const next = d?.next_day && d.next_day <= today ? d.next_day : date < today ? shift(date, 1) : null;46  const strip = (d?.today ?? []).filter((s) => (num(s.total) ?? 0) > 0).slice(0, 8);4748  return (49    <Container wide>50      <BreadcrumbLd items={[{ name: SITE_NAME, href: '/' }, { name: 'Changes', href: '/changes' }, { name: fmtDate(date), href: routes.changesDay(date) }]} />51      <PageHeader52        eyebrow={53          <>54            <Link href={routes.changes()} className="hover:text-ink">55              Changes56            </Link>57            <span aria-hidden>/</span>58            <span>Today in AI</span>59          </>60        }61        title={62          <>63            What changed in AI on <span className="text-ink-2">{fmtDate(date)}</span>64          </>65        }66        lede={d ? <>{fmtInt(total)} events occurred on this UTC day{d.today ? ` across ${present.length} sections` : ''} — grouped when several documents describe one release, generated from the database, nothing editorial.</> : undefined}67        aside={68          <nav className="flex items-center gap-2 text-sm" aria-label="Day navigation">69            <Link href={routes.changesDay(prev)} className="inline-flex h-10 items-center border border-rule px-3 text-ink-2 hover:text-ink" rel="prev">70              ‹ {fmtDate(prev)}71            </Link>72            {next && (73              <Link href={routes.changesDay(next)} className="inline-flex h-10 items-center border border-rule px-3 text-ink-2 hover:text-ink" rel="next">74                {fmtDate(next)} ›75              </Link>76            )}77            {date !== today && (78              <Link href={routes.changesDay(today)} className="inline-flex h-10 items-center px-2 text-accent hover:underline">79                today80              </Link>81            )}82          </nav>83        }84      />85      <div className="pb-16">86        {!d ? (87          <Unavailable what="Daily digest" />88        ) : total === 0 && !d.new_models?.length && present.length === 0 ? (89          <EmptyState title="Nothing occurred on this day">90            {excluded > 0 ? (91              <>92                {fmtInt(excluded)} back-filled historical events fall on this date but are not “today” news — see them on the <Link href={`/timeline?year=${date.slice(0, 4)}&include_backfill=1`} className="link">timeline with backfill</Link> or <Link href={`${routes.changesDay(date)}?include_backfill=1`} className="link">include them here</Link>.93              </>94            ) : (95              'Connectors observed no material change occurring on this UTC day.'96            )}97          </EmptyState>98        ) : (99          <>100            {strip.length > 0 && <DataStrip dense items={strip.map((s) => ({ label: s.label ?? TODAY_LABELS[s.key] ?? s.key, value: fmtInt(s.total), href: `#${s.key.toLowerCase()}` }))} />}101            <p className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-ink-3">102              <span className="inline-flex items-center gap-1">103                Dates are when events <em>occurred</em>104                <Hint align="right" text="occurred_at = effective date when a source states one, else the observation time. Hover any date in the list for the observation time. `today` folds events sharing a group_key (one release seen in several documents) into one row." />105              </span>106              {excluded > 0 && (107                <span data-backfill-excluded>108                  {fmtInt(excluded)} back-filled historical {excluded === 1 ? 'event' : 'events'} excluded ·{' '}109                  <Link href={`/timeline?year=${date.slice(0, 4)}&include_backfill=1`} className="link">110                    see them on the timeline111                  </Link>{' '}112                  ·{' '}113                  <Link href={`${routes.changesDay(date)}${backfill ? '' : '?include_backfill=1'}`} className="link">114                    {backfill ? 'hide backfill' : 'include here'}115                  </Link>116                </span>117              )}118              {backfill && <span className="rounded-[3px] bg-surface-2 px-1.5 text-[10px] uppercase tracking-wide">including backfill</span>}119            </p>120            {present.length > 1 && <SectionNav items={present.map((s) => ({ id: s.key.toLowerCase(), label: s.label ?? TODAY_LABELS[s.key] ?? s.key }))} className="mt-3" />}121122            {d.new_models?.length > 0 && (123              <Section eyebrow="New models" title={<>{fmtInt(d.new_models.length)} new model{d.new_models.length === 1 ? '' : 's'}</>} hairline={false} action={{ href: `/models?year_from=${date.slice(0, 4)}&sort=release`, label: 'Models by release' }}>124                <ul className="grid gap-x-8 sm:grid-cols-2 lg:grid-cols-3">125                  {d.new_models.slice(0, 30).map((m) => (126                    <li key={m.id} className="flex items-baseline justify-between gap-3 border-b border-rule py-2 text-sm">127                      <EntityLink e={m} className="font-medium" />128                      <span className="shrink-0 text-xs text-ink-3">{m.organization?.name ?? ''}</span>129                    </li>130                  ))}131                </ul>132                {d.new_models.length > 30 && <Note className="mt-2">First 30 of {fmtInt(d.new_models.length)}.</Note>}133              </Section>134            )}135136            {present.map((s) => {137              const shown = s.items.length;138              const tot = num(s.total) ?? shown;139              const feed = TODAY_FEED_HREF[s.key]?.(date);140              return (141                <Section key={s.key} id={s.key.toLowerCase()} eyebrow={s.label ?? TODAY_LABELS[s.key] ?? s.key} title={<>{s.label ?? TODAY_LABELS[s.key] ?? s.key} <span className="tnum text-base font-normal text-ink-3">{fmtInt(tot)}</span></>} action={feed ? { href: feed, label: 'Feed' } : undefined}>142                  <ul className="border-t border-rule">143                    {s.items.map((it) => (144                      <TodayItemRow key={it.id} it={it} />145                    ))}146                  </ul>147                  {tot > shown && <Note className="mt-2">First {fmtInt(shown)} of {fmtInt(tot)}{feed ? <> — <Link href={feed} className="link">all in the feed →</Link></> : null}</Note>}148                  {s.items.some((it) => it.importance >= 3) && (149                    <p className="mt-2 flex items-center gap-1.5 text-[11px] text-ink-3">150                      <ImportanceMark importance={3} /> major · <ImportanceMark importance={2} /> important · <ImportanceMark importance={1} /> notable151                    </p>152                  )}153                </Section>154              );155            })}156            <QuietLine quiet={quiet} labels={d.labels} />157            {d.note && <Note className="mt-4">{d.note}</Note>}158          </>159        )}160      </div>161    </Container>162  );163}164